package org.atlantis.grosbeak.pot;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.atlantis.grosbeak.lang.Exceptions;
import org.atlantis.grosbeak.pot.elm.FirstIndex;
import org.atlantis.grosbeak.pot.elm.MaxIndex;
import org.atlantis.grosbeak.pot.elm.Update;
import org.atlantis.grosbeak.pot.transaction.TransactionProvider;

/**
 * 用于执行更新操作的SQL执行器。
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">SeeNoEvil</a>
 * 
 */
class UpdateSQLHandler extends SQLHandler {

	private String preparedSql;
	private int[] argIndex;
	private Method[] argGets;
	private boolean returnAsInt;

	/**
	 * 实例化一个UpdateSQLHandler。
	 * 
	 * @param method
	 */
	public UpdateSQLHandler(Method method) {
		Update update = method.getAnnotation(Update.class);
		String sqlUpdate = update.value();
		if (sqlUpdate.trim().length() == 0)
			throw Exceptions.makeThrow(PotException.class,
					"Missing update language in @Update on method: '%s'",
					method.getName());
		Annotation[] anns = getArgsAnnotations(method);
		for (int i = 0; i < anns.length; i++) {
			Annotation anno = anns[i];
			if (anno.annotationType().equals(FirstIndex.class))
				throw Exceptions.makeThrow(PotException.class,
						"@FirstIndex can not exist in update on method: '%s'",
						method.getName());
			else if (anno.annotationType().equals(MaxIndex.class))
				throw Exceptions.makeThrow(PotException.class,
						"@MaxIndex can not exist in update on method: '%s'",
						method.getName());
		}
		Class<?> returnType = method.getReturnType();
		Class<?>[] paramTypes = method.getParameterTypes();
		if (!returnAsInt && !void.class.equals(returnType))
			throw Exceptions
					.makeThrow(PotException.class,
							"@Update return nothing but void or int.", method
									.getName());
		String[] argNames = PotKit.getParamNames(sqlUpdate);
		this.preparedSql = PotKit.getPreparedSql(sqlUpdate);
		this.argIndex = new int[argNames.length];
		this.argGets = new Method[argNames.length];

		for (int i = 0; i < argNames.length; i++) {
			String argName = argNames[i];
			int pos = getIndexOfArgByName(method, anns, argName);
			argIndex[i] = pos;
			String prop = getPropertyName(argName);
			if (prop != null)
				argGets[i] = getGetterMethod(paramTypes[pos], prop);
		}
	}

	/**
	 * 返回的结果是否是int类型。
	 * 
	 * @return 如果是int类型则返回true，否则返回false。
	 */
	public boolean getReturnAsInt() {
		return this.returnAsInt;
	}

	/**
	 * 进行批处理更新操作。
	 * 
	 * @param txProvider
	 * @param pstmt
	 * @param params
	 * @return
	 */
	public PreparedStatement prepareBatch(TransactionProvider txProvider,
			PreparedStatement pstmt, Object... params) {
		try {
			if (pstmt == null) {
				pstmt = txProvider.getCurrentConnection().prepareStatement(
						preparedSql);
			}
			PotKit.bindParams(pstmt, getSqlParams(argIndex, argGets, params));
			pstmt.addBatch();
			return pstmt;
		} catch (SQLException e) {
			throw Exceptions.wrapThrow(PotException.class, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.atlantis.grosbeak.pot.SQLHandler#handle(org.atlantis.grosbeak.pot
	 * .transaction.TransactionProvider, java.lang.Object[])
	 */
	@Override
	public Object handle(TransactionProvider txProvider, Object... params) {
		return new BaseUpdateCallback<Object>() {
			@Override
			protected Object ctrlResult(PreparedStatement pstmt)
					throws SQLException {
				int result = pstmt.executeUpdate();
				if (returnAsInt)
					return result;
				return null;
			}
		}.execute(txProvider, preparedSql, getSqlParams(argIndex, argGets,
				params));
	}
}
